/*****************************************************************************
* | File      	:   DEV_Config.c
* | Author      :   Waveshare team
* | Function    :   Hardware underlying interface
* | Info        :
*----------------
* |	This version:   V1.0
* | Date        :   2020-02-19
* | Info        :
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documnetation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to  whom the Software is
# furished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
******************************************************************************/
#include "DEV_Config.h"

#ifdef EPD_SPI_ENABLE    
#include <SPI.h>
SPIClass SPI0(EPD_MOSI_PIN,EPD_MISO_PIN,EPD_SCK_PIN,EPD_CS_PIN) ;
#endif


void EPD_BUSY_Callback(void)
{
    /* Prevent unused argument(s) compilation warning */
    detachInterrupt(EPD_BUSY_PIN);
    printf("EPD_BUSY is activated:%d\n", millis());
}


void EPD_BUSY_attachInterrupt(uint8_t mode)
{
    DEV_Delay_ms(5);
    attachInterrupt(EPD_BUSY_PIN, EPD_BUSY_Callback, mode); // 创建中断
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFE);
}


void GPIO_Config(void)
{
    pinMode(EPD_BUSY_PIN,  INPUT);
    pinMode(EPD_RST_PIN , OUTPUT);
    pinMode(EPD_DC_PIN  , OUTPUT);
    
#ifndef EPD_SPI_ENABLE    
    pinMode(EPD_SCK_PIN, OUTPUT);
    pinMode(EPD_MOSI_PIN, OUTPUT);
    pinMode(EPD_CS_PIN , OUTPUT);

    digitalWrite(EPD_CS_PIN , HIGH);
    digitalWrite(EPD_SCK_PIN, LOW);
#endif

// #ifdef EPD154C1
//     attachInterrupt(EPD_BUSY_PIN, EPD_BUSY_Callback, LOW); // 创建中断

// #endif


}
/******************************************************************************
function:	Module Initialize, the BCM2835 library and initialize the pins, SPI protocol
parameter:
Info:
******************************************************************************/
UBYTE DEV_Module_Init(void)
{
	//gpio
	GPIO_Config();

	//serial printf
	// Serial.begin(115200);

	// spi
	// SPI.setDataMode(SPI_MODE0);
	// SPI.setBitOrder(MSBFIRST);
	// SPI.setClockDivider(SPI_CLOCK_DIV4);
	// SPI.begin();

#ifdef EPD_SPI_ENABLE    
    // SPI0.setClockDivider((EPD_CS_PIN),2);
    uint8_t divider=2;
 #ifdef EPD154C1
    // divider=16;
    divider=2;
 #endif

//  #ifdef EPD583Z83
//     // divider=16;
//     divider=4;
//  #endif

 
    SPI0.setClockDivider((EPD_CS_PIN),divider);
    SPI0.begin(EPD_CS_PIN);
#endif

	return 0;
}

/******************************************************************************
function:
			SPI read and write
******************************************************************************/
void DEV_SPI_WriteByte(UBYTE data)
{
    //SPI.beginTransaction(spi_settings);
#ifdef EPD_SPI_ENABLE    
    SPI0.transfer(EPD_CS_PIN,data);
#else

    digitalWrite(EPD_CS_PIN, GPIO_PIN_RESET);

    for (int i = 0; i < 8; i++)
    { 
        if ((data & 0x80) == 0) digitalWrite(EPD_MOSI_PIN, GPIO_PIN_RESET); 
        else                    digitalWrite(EPD_MOSI_PIN, GPIO_PIN_SET);

        data <<= 1;
        digitalWrite(EPD_SCK_PIN, GPIO_PIN_SET);     
        digitalWrite(EPD_SCK_PIN, GPIO_PIN_RESET);
    }

    //SPI.transfer(data);
    digitalWrite(EPD_CS_PIN, GPIO_PIN_SET);
    //SPI.endTransaction();	
#endif   
}


void DEV_SPI_WriteByte(UBYTE *data,UDOUBLE length)
{

#ifdef EPD_SPI_ENABLE    
    SPI0.transfer(EPD_CS_PIN,data,length);
#endif
}


#if 1
static inline uint8_t DEV_SPI_ReadByte()
{
    unsigned char i;
    uint8_t value = 0;

    for (i = 0; i < 8; i++) {
        DEV_Digital_Write(EPD_SCK_PIN, 0);
        DEV_Delay_us(10);
        DEV_Digital_Write(EPD_SCK_PIN, 1);
        DEV_Delay_us(10);
        value <<= 1;
        if (DEV_Digital_Read(EPD_MOSI_PIN) != 0) {
            value |= 1;
        }
    }
    return value;
}


uint8_t EPD_SSD_ReadData()
{
    uint8_t rc;
    // Change SDA Pin to input 
    // SPI0.end();
    // pinMode(EPD_SCK_PIN, OUTPUT);
    pinMode(EPD_MOSI_PIN, INPUT_PULLUP);
    // pinMode(EPD_CS_PIN , OUTPUT);

    // DEV_Digital_Write(EPD_SCK_PIN, 1);
    DEV_Digital_Write(EPD_DC_PIN, 1);
    DEV_Digital_Write(EPD_CS_PIN, 0);



    // read bytes 
    rc = DEV_SPI_ReadByte();
    
    // change back to output
    // DEV_Module_Init();    
    pinMode(EPD_MOSI_PIN, OUTPUT);
    DEV_Digital_Write(EPD_CS_PIN, 1);

    return rc;
}

void EPD_SSD_ReadBytes(uint8_t* buf, uint8_t len)
{
    // Change SDA Pin to input 
    // SPI0.end();
    pinMode(EPD_SCK_PIN, OUTPUT);
    pinMode(EPD_MOSI_PIN, INPUT);
    pinMode(EPD_CS_PIN , OUTPUT);

    DEV_Digital_Write(EPD_SCK_PIN, 0);
    DEV_Digital_Write(EPD_DC_PIN, 1);
    DEV_Digital_Write(EPD_CS_PIN, 0);

    // read bytes 
    for (uint8_t i=0; i<len; i++) {
        buf[i] = DEV_SPI_ReadByte();
    }
    
    // change back to output
    DEV_Module_Init();    
    //DEV_Digital_Write(EPD_DC_PIN, 0);
}


/******************************************************************************
function :	send command
parameter:
     Reg : Command register
******************************************************************************/
static void EPD_SendCommand(UBYTE Reg)
{
    DEV_Digital_Write(EPD_DC_PIN, 0);
    // DEV_Digital_Write(EPD_CS_PIN, 0);
    DEV_SPI_WriteByte(Reg);
    // DEV_Digital_Write(EPD_CS_PIN, 1);
}

/******************************************************************************
function :	send data
parameter:
    Data : Write data
******************************************************************************/

static void EPD_SendData(UBYTE Data)
{
    DEV_Digital_Write(EPD_DC_PIN, 1);
    // DEV_Digital_Write(EPD_CS_PIN, 0);
    DEV_SPI_WriteByte(Data);
    // DEV_Digital_Write(EPD_CS_PIN, 1);
}


// guess the LUT size
int EPD_SSD_LutDetect()
{
#define LUT_LEN_MAX    40//250
#define LUT_FILL       0xa5
    int i;
    EPD_SendCommand(0x32);
    for(i=0; i<LUT_LEN_MAX; i++) {
        EPD_SendData(LUT_FILL);
    }
    EPD_SendCommand(0x33);
    for(i=0; i<LUT_LEN_MAX; i++) {
        uint8_t c = EPD_SSD_ReadData();
        if (c != LUT_FILL) {
            break;
        }
    }
    return i;
}
#endif
